﻿define([
    "dojo/_base/declare"
    , "dojo/_base/array"
    , "dojo/_base/connect"
    , "dojo/dom-construct"
    , "dojo/dom-style"
    , "esri/domUtils"
    , "esri/lang"
    , 'esri/Color'
    , 'esri/graphic'
    , "esri/layers/layer"
    , "esri/layers/GraphicsLayer"
    , "esri/geometry/Point"
    , "esri/geometry/Polygon"
    , "esri/symbols/SimpleFillSymbol"
    , "esri/symbols/SimpleLineSymbol"
  ], function (
    declare
    , ArrayUtils
    , ConnectUtils
    , DomConstructUtils
    , DomStyleUtils
    , DomUtils
    , esriLang
    , Color
    , Graphic
    , Layer
    , GraphicsLayer
    , Point
    , Polygon
    , SimpleFillSymbol
    , SimpleLineSymbol
  ) {
      var Div = declare([], {

          constructor: function (geometry) {
              //dojo.mixin(this, options);

              this._element = null;

              this.geometry = geometry;

              this.init();
          }

          , init: function () {
              this._element = DomConstructUtils.create("div", {
                  style: "position: absolute; left: 0px; top: 0px; background-color1:Red;"
              });
              this._element.graphic = this;
              //DomStyleUtils.set(this._element, {
              //    width: "30px",
              //    height: "30px"
              //});
          }

          , getElement: function () {
              return this._element;
          }
      });



      var DivsClusterLayer = declare([Layer], {

          constructor: function (options) {
             // this.inherited(arguments, [options]);

              this._map = null; //地图对象
              this._element = null; //图层dom元素
              this._connects = []; //事件集合
              this.graphics = [];

              options = options ||{};
              this._showGrid = options.showGrid || false;
              this._gridWidth = options.gridWidth || 32; //栅格宽度（像素）
              this._gridCount = options.gridCount || 0; //栅格数
              this.drawGridDiv = options.drawGridDiv || this.drawGridDiv; //绘制栅格显示的div

              this._vacuate = options.vacuate || false; //抽吸（不显示聚合的数字、显示聚合的第一个点位）

              this.loaded = true;
              this.onLoad(this);
          }

          , _setMap: function (map, layersElement) {
              //map.addLayer会触发
              //console.log("_setMap");

              this._map = map;

              this._element = DomConstructUtils.create("div", {
                  style: "position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px;"
              }, layersElement);
              var thisElement = this._element;
              setTimeout(function () {
                  layersElement.parentNode.appendChild(thisElement);
              }, 1);

              this._connects.push(ConnectUtils.connect(this, "onVisibilityChange", this, this._visibilityChangeHandler));
              this._connects.push(ConnectUtils.connect(map, "onPan", this, this._panHandler));
              this._connects.push(ConnectUtils.connect(map, "onPanEnd", this, this._panEndHandler));
              this._connects.push(ConnectUtils.connect(map, "onZoomStart", this, this._zoomStartHandler));
              this._connects.push(ConnectUtils.connect(map, "onZoomEnd", this, this._zoomEndHandler));
              this._connects.push(ConnectUtils.connect(map, "onExtentChange", this, this._extentChangeHandler));

              return this._element;
          }

          , _unsetMap: function (map, layersElement) {
              //map.removeLayer会触发
              //console.log("_unsetMap");
              if (this._gridLayer) {
                  this._map.removeLayer(this._gridLayer);
              };

              ArrayUtils.forEach(this._connects, ConnectUtils.disconnect, this);
              if (this._element) {
                  //layersElement.removeChild(this._element);
                  this._element.parentNode.removeChild(this._element);
              };

              this._map = null;
              this._element = null;
              this._connects = null;
              this.graphics = null;
          }

          , _visibilityChangeHandler: function (visible) {
              if (visible) {
                  if (this._gridLayer) {
                      this._gridLayer.show();
                  };
                  DomUtils.show(this._element);
              } else {
                  if (this._gridLayer) {
                      this._gridLayer.hide();
                  };
                  DomUtils.hide(this._element);
              };
          }

        , _panHandler: function (delta, extent) {
            //平移时计算移动坐标差
            DomStyleUtils.set(this._element, {
                "transform": "translate(" + extent.x + "px, " + extent.y + "px)"
            });
        }
        , _panEndHandler: function (delta, extent) {
            //平移结束恢复坐标
            DomStyleUtils.set(this._element, {
                "transform": "translate(0px, 0px)"
            });
        }

          , _zoomStartHandler: function (anchor, extent, level, zoomFactor) {
              DomUtils.hide(this._element);
          }
        , _zoomEndHandler: function (anchor, extent, level, zoomFactor) {
            DomUtils.show(this._element);
        }

          , _extentChangeHandler: function (extent, delta) {
              //console.log("_extentChangeHandler");
              this._refresh();
          }

          , add: function (div) {
              this.graphics.push(div);

              var me = this;
              clearTimeout(me._refreshTimeoutId);
              me._refreshTimeoutId = setTimeout(function () { me._refresh(); }, 300);
          }

          , addAll: function (divs) {
              for (var i = 0; i < divs.length; i++) {
                  this.graphics.push(divs[i]);
              };

              this._refresh();
          }

          , _draw: function (div) {
              var map = this.getMap();
              if (map == null) {
                  return;
              };

              if (div._element == null) {
                  return;
              };

              div._element.style.visibility = "hidden";
              this._element.appendChild(div._element);

              var width = div._element.offsetWidth;
              var height = div._element.offsetHeight;

              if (div._element.__remove) {
                  div._element.parentNode.removeChild(div._element);
                  return;
              };

              div._element.style.visibility = "visible";

              var minPoint = new Point(map.extent.xmin, map.extent.ymin, map.spatialReference);
              var maxPoint = new Point(map.extent.xmax, map.extent.ymax, map.spatialReference);
              var screenMinPoint = map.toScreen(minPoint);
              var screenMaxPoint = map.toScreen(maxPoint);

              var x = div.geometry.x;
              var y = div.geometry.y;

              var left = (x - minPoint.x) / (maxPoint.x - minPoint.x) * screenMaxPoint.x;
              var top = (1 - (y - minPoint.y) / (maxPoint.y - minPoint.y)) * screenMinPoint.y;

              //console.log(x + "!" + y + "||" + left + "!" + top);
              var marginLeft = 0 - div._element.offsetWidth / 2;
              var marginTop = 0 - div._element.offsetHeight / 2;

              left = left - width / 2;
              top = top - height / 2;

              DomStyleUtils.set(div._element, {
                  left: left + "px",
                  top: top + "px"
              });

              if (!div.cluster && height > 0) {
                  DomStyleUtils.set(div._element, {
                      "height": height + "px",
                      "font-size": (height - 4) + "px"
                  });
              };
          }

          , clear: function () {
              //console.log("clear");
              var childs = this._element.childNodes;
              for (var i = childs.length - 1; i >= 0; i--) {
                  this._element.removeChild(childs[i]);
              };
              this.graphics = [];
          }

          , _refresh: function () {
              //console.log("_refresh");
              var me = this;

              var map = this.getMap();
              if (map == null) {
                  return;
              };

              var childs = this._element.childNodes;
              for (var i = childs.length - 1; i >= 0; i--) {
                  this._element.removeChild(childs[i]);
              };

              var getSplitWidth = function (width, height, count) {
                  //根据地图的宽度（像素）、高度（像素）、分割的栅格个数，计算每个栅格的边长的像素
                  var long, short;
                  if (width >= height) {
                      long = width;
                      short = height;
                  } else {
                      long = height;
                      short = width;
                  };
                  var splitWidth = short / Math.sqrt(count / (long / short));
                  return splitWidth;
              };

              var getGridList = function (map, count) {
                  //根据地图对象、分割的栅格个数，获取栅格信息的的列表
                  var gridList = [];

                  var width = map.width;
                  var height = map.height;

                  var splitWidth = me._gridWidth;
                  if (me._gridCount > 0) {
                      splitWidth = getSplitWidth(width, height, count);
                  };

                  var xmin = map.extent.xmin;
                  var xmax = map.extent.xmax;
                  var ymin = map.extent.ymin;
                  var ymax = map.extent.ymax;

                  //计算每像素对应的经纬度值
                  var xAdd = (xmax - xmin) / width;
                  var yAdd = (ymax - ymin) / height;

                  //获取地图左下角所在的栅格的左下角坐标，以这个栅格为基准栅格
                  var xmin = (splitWidth * xAdd) * parseInt((xmin - 0) / (xAdd * splitWidth));
                  var ymin = (splitWidth * yAdd) * parseInt((ymin - 0) / (yAdd * splitWidth));

                  //栅格宽度、高度的边角料占栅格的比例
                  var residualWidthRatio = (width % splitWidth) / splitWidth;
                  var residualHeightRatio = (height % splitWidth) / splitWidth;

                  //计算宽度、高度被分割的栅格的个数（如果有边角料大于一般就认为多2个附则是1个）
                  var widthCount = parseInt(width / splitWidth, 10) + (residualWidthRatio * 2 > 1 ? 2 : 1);
                  var heightCount = parseInt(height / splitWidth, 10) + (residualHeightRatio * 2 > 1 ? 2 : 1);

                  var grid;
                  //计算出每个栅格的范围
                  for (var i = 0; i < widthCount; i++) {
                      for (var j = 0; j < heightCount; j++) {
                          grid = {
                              extent: {
                                  xmin: xmin + i * splitWidth * xAdd
                                , xmax: xmin + (i + 1) * splitWidth * xAdd
                                , ymin: ymin + j * splitWidth * yAdd
                                , ymax: ymin + (j + 1) * splitWidth * yAdd
                              },
                              splitWidth: splitWidth,
                              points: []
                          };
                          gridList.push(grid);
                      }
                  }
                  return gridList;
              };

              var contains = function (extent, x, y) {
                  //根据范围，判断坐标是否在范围以内
                  return extent.xmin <= x && x < extent.xmax
                  && extent.ymin <= y && y < extent.ymax;
              };

              var getCenter = function (extent) {
                  return {
                      x: extent.xmin + (extent.xmax - extent.xmin) / 2
                      , y: extent.ymin + (extent.ymax - extent.ymin) / 2
                  };
              };

              var gridList = getGridList(map, this._gridCount);

              if (this._showGrid) {
                  var gridLayer = this._gridLayer;
                  if (!gridLayer) {
                      this._gridLayer = new GraphicsLayer();
                      gridLayer = this._gridLayer;
                      map.addLayer(gridLayer);
                  };
                  gridLayer.clear();
                  for (var j = 0; j < gridList.length; j++) {
                      var grid = gridList[j];
                      var points = [];
                      points.push([grid.extent.xmin, grid.extent.ymin]);
                      points.push([grid.extent.xmin, grid.extent.ymax]);
                      points.push([grid.extent.xmax, grid.extent.ymax]);
                      points.push([grid.extent.xmax, grid.extent.ymin]);


                      var symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
                                new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
                                new Color("#FFFF00"), 0.5),
                                new Color.toDojoColor([255, 255, 0, 0]));
                      var polygon = new Polygon({
                          "rings": [points]
                      });
                      var graphic = new Graphic(polygon, symbol);
                      gridLayer.add(graphic);
                  };
              };

              var div;
              var grid;
              var containsFlag = false;
              for (var i = 0; i < this.graphics.length; i++) {
                  div = this.graphics[i];
                  containsFlag = false;

                  //计算div在哪个栅格
                  for (var j = 0; j < gridList.length; j++) {
                      grid = gridList[j];
                      if (contains(grid.extent, div.geometry.x, div.geometry.y)) {
                          //div在该栅格范围内，就添加到栅格的点位集合
                          grid.points.push(div);
                          containsFlag = true;
                          break;
                      };
                  };

                  if (!containsFlag) {
                      //div不在所有栅格范围内移除
                      this._removeShape({
                          _element: div._element
                      });
                  };
              };

              //绘制每个栅格内的div
              for (var j = 0; j < gridList.length; j++) {
                  grid = gridList[j];
                  if (grid.points.length > 0) {
                      //栅格内有点
                      if (grid.points.length == 1 || this._vacuate) {
                          //栅格内只有一个div就显示div本身
                          div = grid.points[0];
                          this.onGraphicDraw({
                              graphic: {
                                  _element: div._element
                              }
                          });
                          this._draw(div);
                      } else {
                          //超过1个点，显示聚集
                          var ring = [];
                          //移除被聚集的div
                          for (var k = 0; k < grid.points.length; k++) {
                              div = grid.points[k];
                              ring.push([div.geometry.x, div.geometry.y]);
                              this._removeShape({
                                  _element: div._element
                              });
                          };

                          var polygon = new Polygon({
                              "rings": [ring]
                          });
                          var polygonGraphic = new Graphic(polygon);
                          var gridCenter = getCenter(polygon.getExtent());
                          //var gridCenter = getCenter(grid.extent);
                          var pointGeometry = new Point(gridCenter.x, gridCenter.y);

                          div = new Div(pointGeometry);
                          div.cluster = true;
                          div.grid = grid;
                          div.polygon = polygon;
                          me.drawGridDiv({
                              div: div
                          });
                          this.onGraphicDraw({
                              graphic: {
                                  _element: div._element
                              }
                          });
                          this._draw(div);
                      };
                  };
              };

          }

        , drawGridDiv: function (e) {
            var div = e.div;
            var grid = div.grid;

            var divStyle = {
                "padding": "2px 4px"
                , "font-size": "16px"
                , "text-align": "center"
                , "color": "#ffffff"
                , "background-color": "#E34530"
                , "border": "1px solid #8E2B22"
                , "border-radius": "4px"
                , "-webkit-box-shadow": "0 0 5px 0 #8E2B22"
            }; //div的样式
            div.getElement().innerHTML = grid.points.length;
            DomStyleUtils.set(div.getElement(), divStyle);
        }

        , onGraphicDraw: function (event) {

            event.graphic._element.__remove = false;
        }
        , _removeShape: function (graphic) {

            graphic._element.__remove = true;
        }

        , on: function (type, fn) {
            var me = this;
              type = type.toLocaleLowerCase();
            ConnectUtils.connect(this._element, type, this, function (e) {
                var graphicElement = e.target;
                while (graphicElement.parentNode != me._element && graphicElement.parentNode != null) {
                    graphicElement = graphicElement.parentNode;
                };
                if (graphicElement.graphic) {
                    if (graphicElement.graphic.cluster) {
                        var extent = graphicElement.graphic.polygon.getExtent().expand(1.5);
                        me._map.setExtent(extent);
                        return;
                    };
                    var event = {
                        graphic: graphicElement.graphic
                    };
                    fn(event);
                };
            });
        }

      });
      return DivsClusterLayer;
  });